import {
  BadRequestException,
  HttpException,
  HttpStatus,
  Injectable,
  NotFoundException,
} from '@nestjs/common';
import { InjectRepository } from '@nestjs/typeorm';
import { Like, Repository } from 'typeorm';
import { User } from '../../entities/user.entity';
import { CreateUserDto } from './dto/create-user.dto';
import { LoginUserDto } from './dto/login-user.dto';
import * as bcrypt from 'bcryptjs';
import * as svgCaptcha from 'svg-captcha';
import { JwtService } from '@nestjs/jwt';
import { InjectRedis } from '@nestjs-modules/ioredis';
import Redis from 'ioredis';
import { Favorite } from 'src/entities/favorite.entity';
import { UpdateUserDto } from './dto/update-user.dto';
import { FindUserDto } from './dto/find-user.dto';

@Injectable()
export class UserService {
  constructor(
    @InjectRepository(User)
    private userRepository: Repository<User>,
    @InjectRepository(Favorite)
    private favoriteRepository: Repository<Favorite>,
    private readonly jwtService: JwtService,
    @InjectRedis() private readonly redis: Redis,
  ) {}

  /**
   * @description 创建用户
   */
  async create(createUserDto: CreateUserDto) {
    // // 验证验证码
    // const savedCode = await this.redis.get(`verification:${createUserDto.email}`);
    // if (!savedCode || savedCode !== createUserDto.code) {
    //   throw new HttpException('验证码错误或已过期', 400);
    // }
    // 查询数据库是否存在该用户
    const existedUserByName = await this.userRepository.findOne({
      where: [{ username: createUserDto.username }],
    });
    // 如果存在该用户，则返回错误信息
    if (existedUserByName) {
      throw new HttpException('该用户名已存在', 400);
    }
    // 查询数据库是否存在该用户
    const existedUserByEmail = await this.userRepository.findOne({
      where: [{ email: createUserDto.email }],
    });
    if (existedUserByEmail) {
      throw new HttpException('该邮箱已被注册', 400);
    }
    // 创建用户
    const user = new User();
    user.username = createUserDto.username;
    const salt = bcrypt.genSaltSync(10);
    user.password = bcrypt.hashSync(createUserDto.password, salt);
    user.email = createUserDto.email;
    try {
      const savedUser = await this.userRepository.save(user);
      // console.log(savedUser);
      // 创建默认收藏夹
      const defaultFavorite = new Favorite();
      defaultFavorite.name = '默认收藏夹';
      defaultFavorite.description = '这是您的默认收藏夹';
      defaultFavorite.is_default = 1;
      defaultFavorite.user = savedUser;
      await this.favoriteRepository.save(defaultFavorite);
      // 删除Redis中的验证码
      await this.redis.del(`verification:${createUserDto.email}`);
    } catch (error) {
      console.log(error.message);
      throw new HttpException('创建用户失败', 400);
    }
    return {
      message: '注册成功',
    };
  }

  /**
   * @description 删除用户
   */
  async remove(id: number) {
    // 查询数据库是否存在该用户
    const existedUser = await this.userRepository.findOne({
      where: [{ id }],
    });
    // 如果不存在该用户，则返回错误信息
    if (!existedUser) {
      throw new HttpException('该用户不存在', 400);
    }
    // 逻辑删除
    existedUser.is_delete = 1;
    await this.userRepository.save(existedUser);
    return {
      message: '删除成功',
    };
  }

  /**
   * @description 根据id查询用户详情
   */
  async findOne(id: number) {
    // 查询数据库是否存在该用户
    const existedUser = await this.userRepository.findOne({
      where: [{ id }],
    });
    // 如果不存在该用户，则返回错误信息
    if (!existedUser) {
      throw new HttpException('该用户不存在', 400);
    }
    return existedUser;
  }

  /**
   * @description 分页查询用户列表
   */
  async findUserListPaginated(findUserDto: FindUserDto) {
    const { pageNum, pageSize, id, username, email, gender, is_delete } =
      findUserDto;

    // 构建查询条件
    const where: any = {};

    // 默认只查询未删除的用户，除非明确指定查询条件
    where.is_delete = is_delete !== undefined ? is_delete : 0;

    if (id) where.id = id;
    if (username) where.username = Like(`%${username}%`);
    if (email) where.email = Like(`%${email}%`);
    if (gender !== undefined) where.gender = gender;
    if (is_delete !== undefined) where.is_delete = is_delete;

    // 执行分页查询
    const skip = (pageNum - 1) * pageSize;
    const [users, total] = await Promise.all([
      this.userRepository.find({
        where,
        skip,
        take: pageSize,
        order: { created_at: 'DESC' },
      }),
      this.userRepository.count({ where }),
    ]);

    return {
      code: 200,
      message: '获取用户列表成功',
      data: {
        items: users,
        meta: {
          currentPage: pageNum,
          itemsPerPage: pageSize,
          totalItems: total,
          totalPages: Math.ceil(total / pageSize),
        },
      },
    };
  }

  /**
   * 根据用户ID更新用户信息
   * @param userId 用户ID
   * @param updateUserDto 更新的用户信息
   */
  async updateUser(userId: number, updateUserDto: UpdateUserDto) {
    const user = await this.userRepository.findOne({ where: { id: userId } });

    if (!user) {
      throw new NotFoundException(`ID为${userId}的用户不存在`);
    }

    // 如果要更新用户名，需要检查是否已存在
    if (updateUserDto.username && updateUserDto.username !== user.username) {
      const existingUser = await this.userRepository.findOne({
        where: { username: updateUserDto.username },
      });
      if (existingUser) {
        throw new HttpException('该用户名已被使用', HttpStatus.BAD_REQUEST);
      }
    }

    // 如果要更新邮箱，需要检查是否已存在
    if (updateUserDto.email && updateUserDto.email !== user.email) {
      const existingUser = await this.userRepository.findOne({
        where: { email: updateUserDto.email },
      });
      if (existingUser) {
        throw new HttpException('该邮箱已被使用', HttpStatus.BAD_REQUEST);
      }
    }

    // 如果包含密码更新，需要加密处理
    if (updateUserDto.password) {
      const salt = bcrypt.genSaltSync(10);
      updateUserDto.password = bcrypt.hashSync(updateUserDto.password, salt);
    }

    // 更新用户信息
    const updatedUser = Object.assign(user, updateUserDto);
    await this.userRepository.save(updatedUser);

    // 返回更新后的用户信息（不包含密码）
    const { password, ...result } = updatedUser;
    return result;
  }

  /**
   * @description 用户登录
   */
  async login(loginUserDto: LoginUserDto) {
    // // 校验验证码是否正确
    // const isCaptchaValid = await this.validateCaptcha(loginUserDto.captchaKey, loginUserDto.captchaCode);
    // if (!isCaptchaValid) {
    //   throw new HttpException('验证码不正确或已过期', 400);
    // }

    // 查询数据库是否存在该用户
    const existedUser = await this.userRepository.findOne({
      where: [{ email: loginUserDto.email }],
    });
    // 如果不存在该用户，则返回错误信息
    if (!existedUser) {
      throw new HttpException('该用户不存在', 400);
    }
    // 验证密码是否正确
    const isPasswordValid = bcrypt.compareSync(
      loginUserDto.password,
      existedUser.password,
    );
    if (!isPasswordValid) {
      throw new HttpException('密码错误', 400);
    }

    // 生成token
    const token = this.jwtService.sign({
      id: existedUser.id,
      username: existedUser.username,
      email: existedUser.email,
    });
    return {
      token,
    };
  }

  /**
   * @description 获取图形验证码
   */
  async graphicsCode() {
    const captcha = svgCaptcha.create({
      size: 4, // 验证码长度
      noise: 2, // 干扰线条数
      color: true,
      background: '#ffffff',
    });

    const captchaKey = `captcha:${Date.now()}`;
    // 将验证码存入 Redis，设置 5 分钟过期
    await this.redis.set(captchaKey, captcha.text.toLowerCase(), 'EX', 300);

    return {
      captchaKey,
      captchaImage: captcha.data,
    };
  }

  /**
   * @description 验证图形验证码
   */
  async validateCaptcha(
    captchaKey: string,
    captchaCode: string,
  ): Promise<boolean> {
    const savedCaptcha = await this.redis.get(captchaKey);
    if (!savedCaptcha) {
      return false;
    }

    // 验证后删除验证码
    await this.redis.del(captchaKey);
    return savedCaptcha.toLowerCase() === captchaCode.toLowerCase();
  }

  /**
   * 用户退出登录
   * @param userId 用户ID
   */
  async logout(userId: number): Promise<void> {
    // 黑名单键名设计
    const tokenBlacklistKey = `auth:blacklist:user:${userId}`;

    // 使用当前时间戳作为值，用于验证令牌是否在退出后签发
    const currentTime = Date.now();

    // 设置过期时间为JWT令牌的最大有效期，通常是24小时
    const tokenTTL = 24 * 60 * 60; // 24小时，单位秒

    // 将用户ID添加到退出黑名单中
    await this.redis.set(tokenBlacklistKey, currentTime, 'EX', tokenTTL);

    // 记录业务日志
    console.log(`用户 ${userId} 在 ${new Date().toLocaleString()} 退出登录`);
  }

  /**
   * 检查用户是否已退出登录
   * @param userId 用户ID
   * @param tokenIssuedAt 令牌签发时间（Unix时间戳，秒）
   * @returns 如果用户已退出且令牌是在退出前签发的，则返回true
   */
  async isTokenRevoked(
    userId: number,
    tokenIssuedAt: number,
  ): Promise<boolean> {
    const tokenBlacklistKey = `auth:blacklist:user:${userId}`;

    // 获取用户退出时间
    const logoutTimeStr = await this.redis.get(tokenBlacklistKey);

    if (!logoutTimeStr) {
      return false; // 用户未退出
    }

    // 将令牌签发时间转换为毫秒
    const tokenIssuedAtMs = tokenIssuedAt * 1000;

    // 比较令牌签发时间和退出时间
    return tokenIssuedAtMs < parseInt(logoutTimeStr);
  }

  /**
   * 更新用户信息
   * @param userId 用户ID
   * @param updateUserDto 更新的用户信息
   */
  async updateProfile(userId: number, updateUserDto: UpdateUserDto) {
    const user = await this.userRepository.findOne({ where: { id: userId } });

    if (!user) {
      throw new NotFoundException(`ID为${userId}的用户不存在`);
    }

    // 如果要更新用户名，需要检查是否已存在
    if (updateUserDto.username && updateUserDto.username !== user.username) {
      const existingUser = await this.userRepository.findOne({
        where: { username: updateUserDto.username },
      });

      if (existingUser) {
        throw new BadRequestException(
          `用户名 ${updateUserDto.username} 已被使用`,
        );
      }
    }

    // 如果要更新邮箱，需要检查是否已存在
    if (updateUserDto.email && updateUserDto.email !== user.email) {
      const existingUser = await this.userRepository.findOne({
        where: { email: updateUserDto.email },
      });

      if (existingUser) {
        throw new BadRequestException(`邮箱 ${updateUserDto.email} 已被使用`);
      }
    }

    // 如果包含密码更新，需要加密处理
    if (updateUserDto.password) {
      // 假设你有一个加密密码的方法
      const salt = bcrypt.genSaltSync(10);
      const password = bcrypt.hashSync(updateUserDto.password, salt);
      updateUserDto.password = password;
    }

    // 更新用户信息
    const updatedUser = Object.assign(user, updateUserDto);
    await this.userRepository.save(updatedUser);

    // 移除密码等敏感字段
    const { password, ...result } = updatedUser;

    return result;
  }
}
